#include <rtthread.h>
#include <rthw.h>
#include <rtdevice.h>
#include "drv_clock.h"

#define DBG_ENABLE
#define DBG_SECTION_NAME  "[IIC]"
#define DBG_LEVEL         LOG_LVL_DBG
#define DBG_COLOR
#include <rtdbg.h>

#if defined(BSP_USING_I2C2)

#include "drv_gpio.h"

#define I2C2_SCL_PIN        GPIO_PIN_12
#define I2C2_SCL_PORT       GPIO_PORT_E
#define I2C2_SDA_PIN        GPIO_PIN_13
#define I2C2_SDA_PORT       GPIO_PORT_E

enum {
	I2C_ADDR			= 0x000,
	I2C_XADDR			= 0x004,
	I2C_DATA 			= 0x008,
	I2C_CNTR			= 0x00c,
	I2C_STAT			= 0x010,
	I2C_CCR				= 0x014,
	I2C_SRST			= 0x018,
	I2C_EFR				= 0x01c,
	I2C_LCR				= 0x020,
};

enum {
	I2C_STAT_BUS_ERROR	= 0x00,
	I2C_STAT_TX_START	= 0x08,
	I2C_STAT_TX_RSTART	= 0x10,
	I2C_STAT_TX_AW_ACK	= 0x18,
	I2C_STAT_TX_AW_NAK	= 0x20,
	I2C_STAT_TXD_ACK	= 0x28,
	I2C_STAT_TXD_NAK	= 0x30,
	I2C_STAT_LOST_ARB	= 0x38,
	I2C_STAT_TX_AR_ACK	= 0x40,
	I2C_STAT_TX_AR_NAK	= 0x48,
	I2C_STAT_RXD_ACK	= 0x50,
	I2C_STAT_RXD_NAK	= 0x58,
	I2C_STAT_IDLE		= 0xf8,
};
#define SUNXI_GIC_START 16
#define SUNXI_TWI2_PBASE 0x02502800
#define SUNXI_IRQ_TWI2 (43 - SUNXI_GIC_START)
// #define TWI2_SCK GPIOH(5)
// #define TWI2_SDA GPIOH(6)
// #define TWI2_PIN_MUXSEL 4

struct i2c_t113_pdata_t {
	uint32_t virt;
	char * clk;
	int reset;
	int sda;
	int sdacfg;
	int scl;
	int sclcfg;
};

struct hal_i2c_bus
{
    struct rt_i2c_bus_device parent;
    struct rt_mutex lock;
    uint32_t virt;
};

#ifdef BSP_USING_I2C2
static struct hal_i2c_bus _i2c_bus_2;
#endif

#define read32(addr)           (*(volatile unsigned int *)(addr))
#define read8(addr) 			( *(volatile unsigned char *)(addr))
#define write32(addr, value)    (*(volatile unsigned int *)(addr) = (value))
#define write8(addr, value)    (*(volatile unsigned char *)(addr) = (value))

#define TIME_OUT    2

static inline void sdelay(int loops)
{
	__asm__ __volatile__ ("1:\n" "subs %0, %1, #1\n"
		"bne 1b":"=r" (loops):"0"(loops));
}

static int t113_i2c_wait_status(uint32_t virt)
{
	rt_tick_t timeout = rt_tick_get() + TIME_OUT;
	do {
		if((read32(virt + I2C_CNTR) & (1 << 3)))
        {
            return read32(virt + I2C_STAT);
        }
			
	} while(rt_tick_get() < timeout);
	return I2C_STAT_BUS_ERROR;
}

static int t113_i2c_start(uint32_t virt)
{
	uint32_t val;
	val = read32(virt + I2C_CNTR);
	val |= (1 << 5) | (1 << 3);
	write32(virt + I2C_CNTR, val);

	rt_tick_t timeout = rt_tick_get() + TIME_OUT;
	do {
		if(!(read32(virt + I2C_CNTR) & (1 << 5)))
			break;
        
	} while(rt_tick_get() < timeout);
	return t113_i2c_wait_status(virt);
}


static int t113_i2c_stop(uint32_t virt)
{
	uint32_t val;

	val = read32(virt + I2C_CNTR);
	val |= (1 << 4) | (1 << 3);
	write32(virt + I2C_CNTR, val);

	rt_tick_t timeout = rt_tick_get() + TIME_OUT;
	do {
		if(!(read32(virt + I2C_CNTR) & (1 << 4)))
			break;
	} while(rt_tick_get() < timeout);
	return 0;
}


static int t113_i2c_send_data(uint32_t virt, uint8_t dat)
{
	write32(virt + I2C_DATA, dat);
	write32(virt + I2C_CNTR, read32(virt + I2C_CNTR) | (1 << 3));
	return t113_i2c_wait_status(virt);
}

static int t113_i2c_read(uint32_t virt, struct rt_i2c_msg * msg)
{
	uint8_t * p = msg->buf;
	int len = msg->len;

	if(t113_i2c_send_data(virt, (uint8_t)(msg->addr << 1 | 1)) != I2C_STAT_TX_AR_ACK)
		return -1;

	write32(virt + I2C_CNTR, read32(virt + I2C_CNTR) | (1 << 2));
	while(len > 0)
	{
		if(len == 1)
		{
			write32(virt + I2C_CNTR, (read32(virt + I2C_CNTR) & ~(1 << 2)) | (1 << 3));
			if(t113_i2c_wait_status(virt) != I2C_STAT_RXD_NAK)
				return -1;
		}
		else
		{
			write32(virt + I2C_CNTR, read32(virt + I2C_CNTR) | (1 << 3));
			if(t113_i2c_wait_status(virt) != I2C_STAT_RXD_ACK)
				return -1;
		}
		*p++ = read32(virt + I2C_DATA);
		len--;
	}
	return 0;
}

static int t113_i2c_write(uint32_t virt, struct rt_i2c_msg * msg)
{
	uint8_t * p = msg->buf;
	int len = msg->len;

	if(t113_i2c_send_data(virt, (uint8_t)(msg->addr << 1)) != I2C_STAT_TX_AW_ACK)
		return -1;
	while(len > 0)
	{
		if(t113_i2c_send_data(virt, *p++) != I2C_STAT_TXD_ACK)
			return -1;
		len--;
	}
	return 0;
}

static void t113_i2c_set_rate(uint32_t virt, uint32_t rate)
{
	uint32_t pclk = 24000000L;
	int32_t freq, delta, best = 0x7fffffffL;
	int tm = 5, tn = 0;
	int m, n;

	for(n = 0; n <= 7; n++)
	{
		for(m = 0; m <= 15; m++)
		{
			freq = pclk / (10 * (m + 1) * (1 << n));
			delta = rate - freq;
			if(delta >= 0 && delta < best)
			{
				tm = m;
				tn = n;
				best = delta;
			}
			if(best == 0)
				break;
		}
	}
	write32(virt + I2C_CCR, ((tm & 0xf) << 3) | ((tn & 0x7) << 0));
}

static rt_size_t _i2c_master_xfer(struct rt_i2c_bus_device *bus, struct rt_i2c_msg *msgs, rt_uint32_t num)
{
    struct hal_i2c_bus *_i2c_bus = (struct hal_i2c_bus *)bus;
    uint32_t virt = _i2c_bus->virt;
	struct rt_i2c_msg * pmsg = msgs;
	int i, res;

	if(!msgs || num <= 0)
		return 0;
    rt_mutex_take(&_i2c_bus->lock, RT_WAITING_FOREVER);
	if(t113_i2c_start(virt) != I2C_STAT_TX_START){
        return 0;
    }
		
	for(i = 0; i < num; i++, pmsg++)
	{
		if(i != 0)
		{
			if(t113_i2c_start(virt) != I2C_STAT_TX_RSTART)
				break;
		}
		if(pmsg->flags & RT_I2C_RD)
			res = t113_i2c_read(virt, pmsg);
		else
			res = t113_i2c_write(virt, pmsg);
		if(res < 0)
			break;
	}
	t113_i2c_stop(virt);
    rt_mutex_release(&_i2c_bus->lock);
    return i;
}

static void t113_i2c_gpio_init(void)
{
    gpio_set_func(I2C2_SCL_PORT, I2C2_SCL_PIN, IO_FUN_2);
    gpio_set_func(I2C2_SDA_PORT, I2C2_SDA_PIN, IO_FUN_2);
    gpio_set_pull_mode(I2C2_SCL_PORT, I2C2_SCL_PIN, PULL_UP);
    gpio_set_pull_mode(I2C2_SDA_PORT, I2C2_SDA_PIN, PULL_UP);


}

static void t113_i2c_reset_deassert(void)
{
    uint32_t add = T113_CCU_BASE+CCU_TWI_CLK_REG;
    uint32_t reg32 = read32(add);
    reg32 |= 1<<2;
    write32(add, reg32);
    reg32 = read32(add);
    reg32 |= 1<<18;
    write32(add, reg32);
}

void iic2_init(void)
{
    
    t113_i2c_gpio_init();
    t113_i2c_reset_deassert();
    t113_i2c_set_rate(SUNXI_TWI2_PBASE, 400000);//400k
}

static const struct rt_i2c_bus_device_ops _i2c_ops =
{
    _i2c_master_xfer,
    RT_NULL,
    RT_NULL
};

static int _i2c_dev_register(struct hal_i2c_bus* bus, const char* name)
{
    rt_mutex_init(&bus->lock, name, RT_IPC_FLAG_PRIO);

    bus->parent.ops = &_i2c_ops;
    bus->virt = SUNXI_TWI2_PBASE;

    iic2_init();

    write32(bus->virt + I2C_CNTR, 1 << 6);
	write32(bus->virt + I2C_SRST, 1 << 0);

    if (rt_i2c_bus_device_register(&bus->parent, name) != RT_EOK)
    {
        LOG_E("i2c bus register:%s failure\n", name);
        return -1;
    }

    return 0;
}

int rt_hw_i2c_init(void)
{
#ifdef BSP_USING_I2C2
    _i2c_dev_register(&_i2c_bus_2, "i2c2");
#endif
    return 0;
}
INIT_DEVICE_EXPORT(rt_hw_i2c_init);

#endif  /* defined(BSP_USING_I2C0) || defined(BSP_USING_I2C1) */
